# -*- coding utf-8 -*-
import ast
import astunparse

func_def = """def add(x, y):
    return x + y
print(add(3, 5))
"""


class CodeTransformer(ast.NodeTransformer):
    def visit_BinOp(self, node):
        if isinstance(node.op, ast.Add):
            node.op = ast.Sub()
        self.generic_visit(node)
        return node

    def visit_FunctionDef(self, node):
        self.generic_visit(node)
        if node.name == 'add':
            node.name = 'sub'
        args_num = len(node.args.args)
        argslist = []
        replace = {'x': 'a', 'y': 'b'}
        for arg in node.args.args:
            re_id = replace.get(arg.arg, None)
            arg.arg = re_id or arg.arg
            argslist.append(arg.arg)
        args = tuple(argslist)
        func_log_stmt = ''.join(["print('calling func: %s', " % node.name, "'args:'", ", %s" * args_num % args, ")"])
        node.body.insert(0, ast.parse(func_log_stmt))
        return node

    def visit_Name(self, node):
        replace = {'add': 'sub', 'x': 'a', 'y': 'b'}
        re_id = replace.get(node.id, None)
        node.id = re_id or node.id
        self.generic_visit(node)
        return node


r_node = ast.parse(func_def)
transformer = CodeTransformer()
r_node = transformer.visit(r_node)
source = astunparse.unparse(r_node)
print(source)
exec(compile(source, '<string>', 'exec'))
exec(compile(ast.parse(source), '<string>', 'exec'))
